import React, { useEffect, useState } from 'react';
import { NetworkStatus } from '@apollo/client';
import {
  Box,
  Datepicker,
  Grid,
  GridCarousel,
  GridItem,
  Icon,
  ListBase,
  LoadingPlaceholder,
  MainColorType,
  makeToast,
  NotificationBanner,
  Text,
} from '@nova-hf/ui';
import { ErrorBanner } from 'beta/components/error/ErrorBanner';
import { fiberServiceProviderNameMap, formatDate } from 'beta/utils/helpers';
import { parse } from 'date-fns';
import { uniqBy } from 'lodash';
import { inject, observer } from 'mobx-react';
import {
  FiberAppointmentStatus,
  FiberOrderAvailableAppointmentSlots,
  FiberOrderStatus,
  useFiberOrderAppointmentSlotsLazyQuery,
  useFiberOrderQuery,
  useUpdateFiberOrderAppointmentMutation,
} from 'typings/graphql';
import { useTranslation } from 'utils/i18n';

const sortDate = (slots: FiberOrderAvailableAppointmentSlots[]) => {
  return slots
    ?.slice()
    .sort(
      (a, b) => new Date(a.startTime as Date).getDate() - new Date(b.startTime as Date).getDate(),
    );
};

type FiberOrderAppointmentProps = {
  fiberOrderId: string;
  color: MainColorType;
};

const FiberOrderAppointment = ({ fiberOrderId, color }: FiberOrderAppointmentProps) => {
  const { t } = useTranslation('fiber');
  const [isChangingAppointmentTime, setIsChangingAppointmentTime] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState(false);
  const [selectedAppointmentDate, setSelectedAppointmentDate] = useState<Date>();
  const [uniqDates, setUniqDates] = useState<Array<Date>>();
  const [availableAppointments, setAvailableAppointments] =
    useState<FiberOrderAvailableAppointmentSlots[]>();
  const [selectedAppointment, setSelectedAppointment] = useState<
    Pick<FiberOrderAvailableAppointmentSlots, 'id' | 'startTime'> | undefined
  >();

  const [updateFiberOrderAppointment] = useUpdateFiberOrderAppointmentMutation({
    onCompleted() {
      fiberOrderRefetch();
      setIsChangingAppointmentTime(!isChangingAppointmentTime);
      makeToast.success(t('fiberAppointment.updateSuccess'), '');
    },
    onError(error) {
      makeToast.danger(t('fiberAppointment.errorMessages.updateFail'), error.message);
    },
  });

  const {
    data: fiberOrderData,
    loading: fiberOrderLoading,
    error: fiberOrderError,
    refetch: fiberOrderRefetch,
    networkStatus: fiberOrderNetworkStatus,
  } = useFiberOrderQuery({
    variables: {
      input: {
        fiberOrderId: fiberOrderId,
      },
    },
    onCompleted(data) {
      if (
        data.fiberOrder?.fiberAppointment &&
        data.fiberOrder.fiberAppointment.appointmentStatus === FiberAppointmentStatus.Booked
      ) {
        getFiberOrderAppointmentSlots();
      }
    },
  });

  const [
    getFiberOrderAppointmentSlots,
    {
      data: fiberOrderAppointmentSlotsData,
      loading: fiberOrderAppointmentSlotsLoading,
      error: fiberOrderAppointmentSlotsError,
      refetch: fiberOrderAppointmentSlotsRefetch,
      networkStatus: fiberOrderAppointmentSlotsNetworkStatus,
    },
  ] = useFiberOrderAppointmentSlotsLazyQuery({
    variables: {
      input: {
        fiberOrderId: fiberOrderId,
      },
    },
  });

  const fiberOrder = fiberOrderData?.fiberOrder;
  const fiberOrderNeedsVisitAndIsPending =
    fiberOrder?.needsVisit && fiberOrder.status === FiberOrderStatus.Pending;
  const fiberOrderProviderName = fiberServiceProviderNameMap(fiberOrder?.provider ?? '');
  const fiberOrderAppointment = fiberOrderData?.fiberOrder?.fiberAppointment;
  const handleConfirm = async () => {
    if (selectedAppointment?.startTime && fiberOrderAppointment?.externalSlotId) {
      setIsLoading(true);
      await updateFiberOrderAppointment({
        variables: {
          input: {
            fiberOrderId: fiberOrderId,
            startTime: selectedAppointment.startTime,
            externalSlotId: fiberOrderAppointment.externalSlotId,
          },
        },
      });
      setIsLoading(false);
    }
  };

  const fiberOrderAppointmentStatus = fiberOrderAppointment?.appointmentStatus;
  const fiberOrderStatus = fiberOrder?.status;

  const appointmentInfoTextMap = () => {
    if (fiberOrderStatus === FiberOrderStatus.PreOrder) {
      return {
        title: t('fiberAppointment.preOrderTitle'),
        description: t('fiberAppointment.preOrderDescription'),
      };
    }

    if (fiberOrderAppointmentStatus === FiberAppointmentStatus.Booked) {
      return {
        title: t('fiberAppointment.title'),
        description: t('fiberAppointment.description', {
          provider: fiberOrderProviderName,
          date: formatDate(new Date(fiberOrderAppointment?.startTime as Date), 'd.MMM'),
          time: formatDate(new Date(fiberOrderAppointment?.startTime as Date), 'H:mm'),
        }),
      };
    }

    if (fiberOrderAppointmentStatus === FiberAppointmentStatus.Failed) {
      return {
        title: t('fiberAppointment.failedTitle'),
        description: t('fiberAppointment.failedDescription', {
          provider: fiberOrderProviderName,
        }),
      };
    }

    return {
      title: t('fiberAppointment.providerTitle'),
      description: t('fiberAppointment.providerDescription', {
        provider: fiberOrderProviderName,
      }),
    };
  };
  const appointmentInfoText = appointmentInfoTextMap();
  const availableSlots = fiberOrderAppointmentSlotsData?.fiberOrderAppointmentSlots?.availableSlots;

  useEffect(() => {
    if (availableSlots) {
      const getUniqDates = uniqBy(availableSlots, 'startTime').map((appointmentSlots) =>
        parse(
          formatDate(new Date(appointmentSlots.startTime as Date), 'dd.MM.yyyy HH:mm'),
          'dd.MM.yyyy HH:mm',
          new Date(),
        ),
      );
      setSelectedAppointmentDate(getUniqDates[0]);
      setUniqDates(getUniqDates);
    }
  }, [availableSlots]);

  useEffect(() => {
    if (availableSlots && selectedAppointmentDate) {
      const sortedData = sortDate(availableSlots);
      const filteredAppointmentsByDate = sortedData?.filter((sorted) => {
        return (
          new Date(sorted.startTime as Date).getDate() ===
          new Date(selectedAppointmentDate).getDate()
        );
      });
      setAvailableAppointments(filteredAppointmentsByDate);
    }
  }, [selectedAppointmentDate]);

  const hasInstallationOptions = availableAppointments && !!availableAppointments.length;

  const showChangeAppointmentButton =
    fiberOrderStatus !== FiberOrderStatus.PreOrder &&
    !isChangingAppointmentTime &&
    fiberOrderAppointmentStatus === FiberAppointmentStatus.Booked;

  const showConfirmCancelChangeAppointmentButton =
    isChangingAppointmentTime && !fiberOrderAppointmentSlotsError;

  const showAvailableAppointmentSlots =
    hasInstallationOptions &&
    !fiberOrderAppointmentSlotsError &&
    !fiberOrderAppointmentSlotsLoading;

  if (!fiberOrderNeedsVisitAndIsPending && fiberOrderStatus !== FiberOrderStatus.PreOrder)
    return null;

  if (fiberOrderLoading || fiberOrderError) {
    return (
      <>
        <ErrorBanner
          eyebrowTexts={[
            t('errors:contractList.netid.eyebrows.1'),
            t('errors:contractList.netid.eyebrows.2'),
            t('errors:contractList.netid.eyebrows.3'),
          ]}
          titles={[
            t('errors:contractList.netid.titles.1'),
            t('errors:contractList.netid.titles.2'),
            t('errors:contractList.netid.titles.3'),
          ]}
          descriptions={[
            t('errors:contractList.netid.descriptions.1'),
            t('errors:contractList.netid.descriptions.2'),
            t('errors:contractList.netid.descriptions.3'),
          ]}
          icon="zap"
          color={color}
          showLoading={fiberOrderLoading || fiberOrderNetworkStatus === NetworkStatus.refetch}
          refetchButton={{
            text: t('errors:buttons.refresh'),
            icon: 'refresh',
            onClick: () => fiberOrderRefetch(),
          }}
          loadingComponent={
            <>
              <ListBase isLoading gap={3} />
              <ListBase isLoading gap={3} />
              <ListBase isLoading />
            </>
          }
        />
      </>
    );
  }
  return (
    <NotificationBanner
      icon={'home'}
      eyebrowText={t('fiberAppointment.eyebrow')}
      title={appointmentInfoText.title}
      hasPingAlert
      color={color}
      description={appointmentInfoText.description}
      {...(showChangeAppointmentButton && {
        mainButtonColored: {
          text: t('fiberAppointment.changeButton'),
          icon: 'longArrowRight',
          onClick: () => setIsChangingAppointmentTime(!isChangingAppointmentTime),
        },
      })}
      {...(showConfirmCancelChangeAppointmentButton &&
        !fiberOrderAppointmentSlotsError && {
          mainButtonWhite: {
            text: t('fiberAppointment.cancelButton'),
            icon: 'close',
            onClick: () => setIsChangingAppointmentTime(!isChangingAppointmentTime),
          },
          mainButtonColored: {
            text: t('fiberAppointment.confirmButton'),
            icon: 'checkBox',
            onClick: () => handleConfirm(),
            isLoading,
          },
        })}
    >
      {isChangingAppointmentTime && (
        <>
          {fiberOrderAppointmentSlotsLoading ? (
            <Grid gridTemplate={{ sm: 2, lg: 4, xl: 5 }} columnGap={{ sm: 2, lg: 3 }}>
              {[...Array(3)]?.map((_, i) => (
                <GridItem key={i} gridColumn={{ sm: 'span1' }}>
                  <LoadingPlaceholder width="100%" height={11} />
                </GridItem>
              ))}
            </Grid>
          ) : fiberOrderAppointmentSlotsError ? (
            <NotificationBanner
              color={color}
              icon="warning"
              eyebrowText={t('fiberAppointment.errorMessages.eyebrow')}
              title={t('fiberAppointment.errorMessages.title')}
              description={t('fiberAppointment.errorMessages.description')}
              mainButtonColored={{
                text: t('fiberAppointment.refreshButton'),
                icon: 'refresh',
                onClick: () =>
                  fiberOrderAppointmentSlotsRefetch &&
                  fiberOrderAppointmentSlotsRefetch({
                    input: { fiberOrderId },
                  }),
                isLoading:
                  fiberOrderAppointmentSlotsLoading ||
                  fiberOrderAppointmentSlotsNetworkStatus === NetworkStatus.refetch,
              }}
            />
          ) : (
            <>
              <Datepicker
                selected={selectedAppointmentDate}
                onSelect={(date) => setSelectedAppointmentDate(date)}
                inputId={'date'}
                inputName={'date'}
                minDate={uniqDates && uniqDates[0]}
                color={color}
                includeDates={uniqDates}
              />
              <Text variant="subtitleBold" marginTop={5} marginBottom={3}>
                {t('fiberAppointment.selectTime')}
              </Text>

              {showAvailableAppointmentSlots && (
                <GridCarousel
                  color={color}
                  numberOnSlide={5}
                  gridTemplate={{ sm: 3, lg: 4, xl: 4 }}
                  gridGap={{ sm: 2, lg: 3 }}
                  backButtonHiddenTitle={t('fiberAppointment.previousPage')}
                  nextButtonHiddenTitle={t('fiberAppointment.nextPage')}
                >
                  {availableAppointments?.map((appointment, i) => {
                    return (
                      <GridItem key={i} gridColumn={{ sm: 'span1' }}>
                        <Box
                          renderAs="button"
                          width="100%"
                          display="flex"
                          justifyContent="space-around"
                          alignItems="center"
                          paddingX={3}
                          paddingY={4}
                          borderColor={{
                            hover: color,
                            focusVisible: color,
                            sm: selectedAppointment?.id === appointment.id ? color : 'grey200',
                          }}
                          borderStyle="solid"
                          borderWidth="2px"
                          onClick={() => setSelectedAppointment(appointment)}
                          dottedShadow={{ sm: 'none', md: 'small' }}
                          backgroundColor="white"
                        >
                          <Icon icon="history" color={color} />
                          <Text variant="pLargeBold">
                            {formatDate(new Date(appointment.startTime as Date), 'H:mm')}
                          </Text>
                        </Box>
                      </GridItem>
                    );
                  })}
                </GridCarousel>
              )}
            </>
          )}
        </>
      )}
    </NotificationBanner>
  );
};

export default inject('authentication')(observer(FiberOrderAppointment));
